package com.example.downloader.upload;

import android.os.AsyncTask;
import android.util.Log;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class Task<R> implements UploadProgress {
    private int mProgress;
    static final int WAITING = 0;
    static final int COMPRESS = 1;
    static final int UPLOAD = 2;
    static final int FINISHED = 3;
    static final int ERROR = -1;
    static final int CANCEL = -2;
    private final List<Callback> mCallbacks = new ArrayList();
    private int mStatus = WAITING;
    private final TaskExecutor<R> mLoader;
    private R mResult;
    private WorkTask mTask;
    private TaskQueue<R> mTaskQueue;
    private final String mPath;

    public Task(TaskExecutor<R> loader, TaskQueue<R> taskQueue, String path) {
        mLoader = loader;
        mTaskQueue = taskQueue;
        mPath = path;
    }

    @Override
    public String toString() {
        return mPath;
    }

    @Override
    public int hashCode() {
        return mPath.hashCode();
    }

    String getPath() {
        return mPath;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == this)
            return true;

        if (obj instanceof Task) {
            return ((Task) obj).mPath.equals(mPath);
        } else if (obj instanceof String) {
            return obj.equals(mPath);
        }
        return super.equals(obj);
    }

    void execute() {
        if (mTask == null) {
            mTask = new WorkTask();
            mTask.execute(mPath);
        }
    }

    private void finish() {
        Iterator<Callback> iterator = mCallbacks.iterator();
        while (iterator.hasNext()) {
            final Callback callback = iterator.next();
            callback.detachTask();
            iterator.remove();
            mTaskQueue.popTaskCallback(callback.getId());
        }
        mTask = null;
        mTaskQueue.popTask(mPath);
    }

    void cancel() {
        if (mTask != null && !mTask.isCancelled()) {
            mTask.cancel(true);
        }
    }


    void addCallback(Callback<R> callback) {
        mCallbacks.add(callback);
    }

    void removeCallback(Callback<R> callback) {
        mCallbacks.remove(callback);
    }

    boolean callAndAddCallback(Callback<R> callback) {
//        if (!isActive())
//            return false;
        switch (mStatus) {
            case WAITING:
                callback.onWaiting();
                break;
            case COMPRESS:
                callback.onCompressing();
                break;
            case UPLOAD:
                callback.onUploading(mProgress);
                break;
            case FINISHED:
                callback.onFinished(mResult);
                break;
            case ERROR:
                callback.onError();
                break;
            case CANCEL:
                callback.onCancel();
                break;
        }
        addCallback(callback);
        return true;
    }


    @Override
    public void setUploadProgress(int progress) {
        if (mTask != null) {
            mTask.setUploadProgress(progress);
        }
    }

    boolean isActive() {
        return mStatus != FINISHED && mStatus != ERROR && mStatus != CANCEL;
    }

    private class WorkTask extends AsyncTask<String, Integer, R> {
        @Override
        protected void onPostExecute(R t) {
            if (mStatus != ERROR) {
                mStatus = FINISHED;
                mResult = t;
                for (Callback callback : mCallbacks) {
                    callback.onFinished(t);
                }
                finish();
            }

        }

        @Override
        protected void onCancelled(R r) {
            super.onCancelled(r);
            if (mStatus != ERROR) {
                mStatus = CANCEL;
                for (Callback callback : mCallbacks) {
                    callback.onCancel();
                }
                finish();
            }

        }


        @Override
        protected void onPreExecute() {
            mStatus = WAITING;
            for (Callback<R> callback : mCallbacks) {
                callback.onWaiting();
            }
        }

        void onError() {
            mStatus = ERROR;
            for (Callback callback : mCallbacks) {
                callback.onError();
            }
            finish();
        }

        void onCompress() {
            mStatus = COMPRESS;
            for (Callback callback : mCallbacks) {
                callback.onCompressing();
            }

        }

        void onUpload(int progress) {
            mStatus = UPLOAD;
            mProgress = progress;
            for (Callback callback : mCallbacks) {
                callback.onUploading(mProgress);
            }
        }

        @Override
        protected void onProgressUpdate(Integer... values) {
            super.onProgressUpdate(values);
            switch (values[0]) {
                case COMPRESS:
                    onCompress();
                    break;
                case UPLOAD:
                    onUpload(values[1]);
                    break;
                case ERROR:
                    onError();
                    break;

            }

        }

        @Override
        protected R doInBackground(String... strings) {
            final String filePath = strings[0];
            try {
                publishProgress(COMPRESS);
                final String compressingPath = mLoader.onCompressing(filePath);
                return mLoader.onUploading(compressingPath, Task.this);
            } catch (Throwable throwable) {
                throwable.printStackTrace();
                publishProgress(ERROR);

            }


            return null;
        }

        public void setUploadProgress(int progress) {
            publishProgress(UPLOAD, progress);
//            Log.i("@@", "@@@@@@:" + mPath + "," + mCallbacks.size());
        }
    }
}
